package DispatchSys;

import java.util.Iterator;
import java.util.List;
import Shared.Map;

/**
 * The Assigner module, see Architecture report for specifications.
 * 
 */
public class Assigner {
	/**
	 * Module that assigns an ambulance to an emergency
	 */

	// Database
	private final Database db;

	// Map of the city
	private final Map map;

	/**
	 * Creates a new Assigner
	 */
	public Assigner() {

		this.db = Database.getInstance();
		this.map = Map.getInstance();

	}

	/**
	 * Assign the first emergency of the list of all emergencies
	 * 
	 * @return the resulting assignment or null if there aren't emergencies to
	 *         be assigned anymore or there are no free ambulances
	 */
	public Assignment assign() {

		// Takes first emergency
		ERInfo e = dequeueER();
		if (e == null)
			return null;

		// Assign it to the ambulance
		AmbulanceInfo a = assignER(e);
		if (a == null)
			return null;

		return new Assignment(e, a);
	}

	/**
	 * Return an emergency unassigned from the virtual priority queue stored
	 * into the database, according to the priority decision (secret). If the
	 * first one is urgent and there aren't equipped ambulances available,
	 * returns the first unassigned emergency that is not urgent.
	 */
	private ERInfo dequeueER() {

		boolean existsEquippedAmbulance = db.equipedAmbulanceAvailable();
		
		for (ERInfo current : db.getAllUnassignedER()) {
			if (current.isUrgent() && existsEquippedAmbulance)
				return current;

			if (!current.isUrgent())
				return current;
		}

		return null;

	}

	/**
	 * Return an ambulanceInfo chosen among the free ambulances, according to a
	 * priority rule, and update the Database status of this emergency. The
	 * ambulance chosen is the closest one to the emergency
	 * 
	 * @param er
	 *            - the emergency to be assigned
	 * @return
	 */
	private AmbulanceInfo assignER(ERInfo er) {

		List<AmbulanceInfo> ambs;

		if (er.isUrgent())
			ambs = this.db.getFreeEquipedAmbulances();
		else
			ambs = this.db.getFreeAmbulances();

		Iterator<AmbulanceInfo> it = ambs.iterator();

		if (!it.hasNext())
			return null;

		AmbulanceInfo closerAmb = it.next();
		double smallestDistance = map.computeDistance(map.getNearestPos(closerAmb.getPosition()),
				map.getNearestPos(er.getLocation()));
		// Search for the closest ambulance
		while (it.hasNext()) {
			AmbulanceInfo current = it.next();
			double currentDist = map.computeDistance(map.getNearestPos(er.getLocation()),
					map.getNearestPos(current.getPosition()));
			if (currentDist < smallestDistance) {
				smallestDistance = currentDist;
				closerAmb = current;
			}
		}

		return closerAmb;

	}
}
